Skip to content

feat(providers): add Google Veo API package#80

Merged
Kamilbenkirane merged 3 commits intomainfrom
api/google_veo
Dec 15, 2025
Merged

feat(providers): add Google Veo API package#80
Kamilbenkirane merged 3 commits intomainfrom
api/google_veo

Conversation

@Kamilbenkirane
Copy link
Copy Markdown
Member

Summary

Adds the celeste-google.veo API module for Google's Veo video generation API, following the same mixin pattern as GenerateContent and Imagen. Migrates the video-generation capability to use this shared API layer.

Architecture

packages/providers/google/
└── celeste_google/
    ├── generate_content/   # Text generation API (existing)
    ├── imagen/             # Image generation API (existing)
    └── veo/                # Video generation API (NEW)
        ├── client.py       # GoogleVeoClient mixin
        ├── parameters.py   # API-level parameter mappers
        └── config.py       # Endpoints, polling config

packages/capabilities/video-generation/
└── providers/google/
    ├── client.py           # Now extends GoogleVeoClient
    └── parameters.py       # Extends base Veo mappers

Changes

1. Google Veo API (celeste-google.veo)

Client Mixin:

  • HTTP POST to /v1beta/models/{model_id}:predictLongRunning
  • Async polling for long-running video generation operations
  • Parse generatedSamples[0].video from response
  • Download video content from GCS URLs (gs://https://storage.googleapis.com/)

Parameters:

  • AspectRatioMapper: parameters.aspectRatio
  • ResolutionMapper: parameters.resolution
  • DurationSecondsMapper: parameters.durationSeconds
  • ReferenceImagesMapper: instances.referenceImages with base64 encoding
  • FirstFrameMapper: instances.image for image-to-video
  • LastFrameMapper: instances.lastFrame for interpolation

Config:

  • Polling interval: 10s
  • Default timeout: 300s (5 minutes for long-running ops)

2. Video Generation Migration

  • GoogleVideoGenerationClient now extends GoogleVeoClient mixin
  • Delegates HTTP, polling, downloads to API layer
  • Removed config.py (now in API layer)
  • Added celeste-google as workspace dependency

3. Core Utility

  • Added image_to_data_uri() function in celeste.utils
  • Converts ImageArtifact to base64 data URI for API requests

Breaking Changes

Parameter Mapper Rename

Old Name New Name Reason
DurationSecondsMapper DurationMapper Capability uses VideoGenerationParameter.DURATION

The API layer uses DurationSecondsMapper (matching the API field durationSeconds), while the capability layer uses DurationMapper (matching the capability parameter DURATION). This follows the established pattern where capability mappers have simplified names.

Test Plan

  • All unit tests pass
  • Type checking passes (mypy)
  • Linting passes (ruff)
  • Security scan passes (bandit)
  • Integration tests with real API calls

## Client (GoogleVeoClient mixin)
- HTTP POST to /v1beta/models/{model_id}:predictLongRunning endpoint
- Async polling for long-running video generation operations
- Parse generatedSamples[0].video from response
- Download video content from GCS URLs

## Parameters
- AspectRatioMapper: parameters.aspectRatio
- ResolutionMapper: parameters.resolution
- DurationSecondsMapper: parameters.durationSeconds
- ReferenceImagesMapper: instances.referenceImages with base64 encoding
- FirstFrameMapper: instances.image for image-to-video
- LastFrameMapper: instances.lastFrame for interpolation

## Config
- API base URL: https://generativelanguage.googleapis.com
- Endpoints: predictLongRunning, operation polling
- Polling interval: 10s, timeout: 300s
- GCS storage URL for video downloads
- Update GoogleVideoGenerationClient to extend GoogleVeoClient mixin
- Simplify client by delegating HTTP, polling, downloads to API layer
- Update parameter mappers to extend base Veo API mappers
- Remove config.py (now in API layer)
- Add celeste-google as workspace dependency
- Fix _create_inputs to get prompt from parameters dict
Converts ImageArtifact to base64 data URI string for API requests
that require inline image data (e.g., Google Veo reference images).
@claude
Copy link
Copy Markdown

claude Bot commented Dec 15, 2025

Code Review - PR #80: Google Veo API Package

Summary

This PR successfully extracts Google Veo video generation into a reusable API layer following the established mixin pattern (GenerateContent, Imagen). The architecture is clean and the refactoring significantly reduces code duplication. Overall, this is a well-executed refactoring with strong architectural alignment.


✅ Strengths

Architecture & Design

  • Excellent mixin pattern usage: GoogleVeoClient follows the same pattern as GoogleImagenClient, providing clean separation of concerns
  • Code reuse: Reduced from 360 deleted lines to 195 new lines in the API layer - significant DRY improvement
  • Clear separation: API layer handles HTTP/polling/downloads; capability layer handles artifact wrapping
  • Consistent naming: Follows established conventions (DurationSecondsMapper at API level, DurationMapper at capability level)

Code Quality

  • Clean delegation: The capability client properly delegates to the mixin via super() calls
  • Good error handling: Proper error messages and validation throughout
  • Type hints: Good use of type hints with type: ignore comments for mixin attributes
  • Logging: Appropriate logging at INFO and DEBUG levels for operations and polling

@claude
Copy link
Copy Markdown

claude Bot commented Dec 15, 2025

🔍 Issues & Concerns (Part 1)

1. Security: Infinite Polling Loop ⚠️ HIGH PRIORITY

Location: packages/providers/google/src/celeste_google/veo/client.py:69-91

The polling loop has no timeout mechanism. If the API never returns done=True, this will poll forever.

Issue:

while True:
    await asyncio.sleep(config.POLL_INTERVAL)
    # ... polling logic
    if operation_data.get("done"):
        break

Recommendation: Add timeout with elapsed time tracking to prevent infinite loops.


2. Error Handling: Silent JSON Parse Failures

Location: packages/providers/google/src/celeste_google/veo/client.py:61,80

If the response is not valid JSON, this will raise an exception that's not caught. While _handle_error_response checks HTTP status, malformed JSON responses could cause unclear error messages.

Recommendation: Add explicit JSON parsing with better error messages using try/except with json.JSONDecodeError.


3. Error Messages: Data URI Parsing

Location: packages/providers/google/src/celeste_google/veo/parameters.py:104-106,141-143,181-183

Using "from None" suppresses the original exception chain, making debugging harder. The error messages are also generic.

Current:

except (ValueError, IndexError, OSError):
    msg = "Failed to process reference image. Ensure valid data/path/url."
    raise ValidationError(msg) from None

Recommendation: Preserve exception chains and provide more specific error messages.

@claude
Copy link
Copy Markdown

claude Bot commented Dec 15, 2025

🔍 Issues & Concerns (Part 2)

4. Potential Bug: URL Handling in image_to_data_uri

Location: src/celeste/utils.py:8-32

The function only handles data or path attributes, but ImageArtifact also has a url attribute. If an artifact has only a URL (e.g., from a previous generation), this function will silently fail with an unclear error.

Recommendation: Add explicit handling for the url case with a clear error message explaining that URL-based images need to be downloaded first.


5. Performance: Redundant Operations in Parameter Mapping

Location: packages/providers/google/src/celeste_google/veo/parameters.py:95-103

The code converts to data URI string, then immediately parses it back:

data_uri = image_to_data_uri(img)
header, encoded = data_uri.split(",", 1)
mime_type = header.split(":")[1].split(";")[0]

Recommendation: Consider creating a separate function that returns the components directly to avoid the string concatenation and parsing overhead.


6. Type Safety: Missing Return Type Annotation

Location: packages/providers/google/src/celeste_google/veo/client.py:99

Using Any as return type loses type safety. The function clearly returns a dict.

Current: def _parse_content(self, response_data: dict[str, Any]) -> Any:

Recommended: def _parse_content(self, response_data: dict[str, Any]) -> dict[str, Any]:

@claude
Copy link
Copy Markdown

claude Bot commented Dec 15, 2025

🧪 Test Coverage

Missing Tests

The PR doesn't include unit tests for the new GoogleVeoClient mixin or parameter mappers. While integration tests exist, consider adding:

  1. Unit tests for GoogleVeoClient._parse_content():

    • Valid response with video data
    • Missing generatedSamples
    • Missing video key
  2. Unit tests for parameter mappers:

    • Valid inputs
    • Invalid image artifacts (no data/path)
    • Data URI parsing edge cases
  3. Unit tests for image_to_data_uri():

    • With data attribute
    • With path attribute
    • With neither (error case)
    • Different mime types
  4. Polling timeout tests (if timeout is added):

    • Normal completion
    • Timeout exceeded
    • API error during polling

📊 Performance Considerations

Polling Efficiency

  • Current: 10-second intervals with no backoff
  • Consideration: For very long operations, consider exponential backoff to reduce API calls (e.g., cap at 60s)

Memory Usage

  • Video downloads: The download_content() method loads entire video into memory. For large videos (>100MB), this could be problematic.
  • Consideration: Consider streaming or chunked downloads for large files

@claude
Copy link
Copy Markdown

claude Bot commented Dec 15, 2025

🔐 Security Considerations

✅ Good Practices

  • API Key Exposure: API keys are properly handled via self.auth.get_headers() - no hardcoded credentials
  • Input Validation: Good validation via _validate_value() in mappers with proper error messages
  • Injection Risks: No string concatenation with user input in API endpoints; JSON serialization handled safely

📝 Breaking Changes

The parameter mapper rename is well-documented in the PR description:

  • DurationSecondsMapper → DurationMapper at capability level
  • This is a reasonable breaking change with clear migration path

Recommendation: Ensure this is documented in CHANGELOG/release notes.


🎯 Recommendations Summary

Must Fix (High Priority)

  1. ⚠️ Add timeout to polling loop to prevent infinite loops
  2. ⚠️ Improve error handling for JSON parsing

Should Fix (Medium Priority)

  1. Add unit tests for new code (client mixin and parameter mappers)
  2. Improve error messages (preserve exception chains)
  3. Handle URL-based ImageArtifacts explicitly in image_to_data_uri

Nice to Have (Low Priority)

  1. Optimize data URI handling to avoid redundant operations
  2. Add type annotations (replace Any with specific types)
  3. Improve documentation with requirements section for mixin
  4. Consider exponential backoff for polling

@claude
Copy link
Copy Markdown

claude Bot commented Dec 15, 2025

✅ Final Assessment

This PR demonstrates excellent architectural thinking and code organization. The refactoring successfully achieves its goals of code reuse and consistency by:

  • Following the established mixin pattern used in other Google API packages
  • Significantly reducing code duplication (360 deleted vs 195 added lines)
  • Creating a clean separation between API-level and capability-level concerns
  • Maintaining type safety and good error handling practices

The implementation quality is high, with only a few issues that need addressing before merge:

Critical: The infinite polling loop needs a timeout mechanism to prevent hanging indefinitely if the API doesn't respond as expected.

Important: Adding unit tests will ensure the refactored code is robust and maintainable.

Once the infinite polling loop issue is addressed and tests are added, this will be ready to merge.


Status: ✅ Approve with requested changes (fix infinite loop, add tests)

Great work on this refactoring! 🎉


File-specific references:

  • Polling loop: packages/providers/google/src/celeste_google/veo/client.py:69-91
  • Parameter mappers: packages/providers/google/src/celeste_google/veo/parameters.py
  • Utility function: src/celeste/utils.py:8-32
  • Capability client: packages/capabilities/video-generation/src/celeste_video_generation/providers/google/client.py

@Kamilbenkirane Kamilbenkirane merged commit f38d736 into main Dec 15, 2025
18 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant